package com.tianji.promotion.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.domain.dto.CouponFormDTO;
import com.tianji.promotion.domain.dto.CouponIssueFormDTO;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.CouponScope;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.domain.query.CouponQuery;
import com.tianji.promotion.domain.vo.CouponPageVO;
import com.tianji.promotion.domain.vo.CouponVO;
import com.tianji.promotion.enums.*;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.service.ICouponScopeService;
import com.tianji.promotion.service.ICouponService;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.tianji.promotion.enums.UserCouponStatus.UNUSED;

/**
 * <p>
 * 优惠券的规则信息 服务实现类
 * </p>
 *
 * @author wxh
 * @since 2024-09-01
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {
    private final ICouponScopeService scopeService;
    private final IExchangeCodeService codeService;
    private final IUserCouponService userCouponService;

    /**
     * 新增优惠券
     *
     * @param couponFormDTO
     */
    @Override
    @Transactional
    public void addCoupon(CouponFormDTO couponFormDTO) {
        //1.转为po
        Coupon coupon = BeanUtils.copyBean(couponFormDTO, Coupon.class);
        //2.新增
        save(coupon);
        //3.判断是否有使用范围
        if (!couponFormDTO.getSpecific()) {
            //没有范围
            return;
        }
        //有范围限制
        //新增成功拿到优惠券的id
        Long couponId = coupon.getId();
        //4.插入到中间表（coupon_scope）
        //4.1拿到对用也业务id（是对那些类型使用）
        List<Long> bizIds = couponFormDTO.getScopes();
        if (ObjectUtil.isEmpty(bizIds)) {
            throw new BizIllegalException("优惠券没有使用范围");
        }
        //4.2通过stream流拿到对应的bizid给CouponScope赋值
        List<CouponScope> scopeList =
                bizIds.stream().map(bizId -> new CouponScope().setBizId(bizId).setCouponId(couponId)).collect(Collectors.toList());
        //4.3收集到了中间表类型的集合，批量插入
        scopeService.saveBatch(scopeList);

    }

    /**
     * 分页查询优惠券
     *
     * @param query
     * @return
     */
    @Override
    public PageDTO<CouponPageVO> pageQueryConpon(CouponQuery query) {
        //1.直接查询
        Page<Coupon> page = this.lambdaQuery()
                .eq(ObjectUtil.isNotEmpty(query.getType()), Coupon::getDiscountType, query.getType())
                .eq(ObjectUtil.isNotEmpty(query.getStatus()), Coupon::getStatus, query.getStatus())
                .like(StringUtils.isNotBlank(query.getName()), Coupon::getName, query.getName())//加空格的空串也能判别
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        //1.1判断返回的数据是否为空
        List<Coupon> records = page.getRecords();
        if (ObjectUtil.isEmpty(records)) {
            //1.1.1为空则返回一个空集合
            return PageDTO.empty(page);
        }
        //2.对象拷贝
        List<CouponPageVO> voList = BeanUtils.copyList(records, CouponPageVO.class);
        //3.数据返回
        return PageDTO.of(page, voList);
    }

    /**
     * 开始发放优惠券
     *
     * @param couponIssueFormDTO 发放优惠券修改的参数
     */
    @Override
    @Transactional
    public void beginIssue(CouponIssueFormDTO couponIssueFormDTO) {
        //1.查询优惠券是否存在
        Coupon coupon = this.getById(couponIssueFormDTO.getId());
        if (ObjectUtil.isEmpty(coupon)) {
            throw new BadRequestException("优惠券信息不存在");
        }
        //1.1判断优惠券状态，只有是待发放或者暂停才能发放
        CouponStatus status = coupon.getStatus();
        if (status != CouponStatus.DRAFT && status != CouponStatus.PAUSE) {
            throw new BadRequestException("优惠券不是待发放或暂停状态，不能发放");
        }
        //1.2获取该优惠券是否有开始时间
        LocalDateTime issueTime = couponIssueFormDTO.getIssueBeginTime();
        //2判断是否有开始时间，或者开始时间在系统时间之前都要立即发放
        boolean isTure = issueTime == null || !issueTime.isAfter(LocalDateTime.now());
        //2.0对象拷贝
        Coupon c = BeanUtils.copyBean(couponIssueFormDTO, Coupon.class);
        if (isTure) {
            //2.1需要立刻发放
            //修改状态
            c.setStatus(CouponStatus.ISSUING);
            c.setIssueBeginTime(LocalDateTime.now());
        } else {
            c.setStatus(CouponStatus.UN_ISSUE);
        }
        //3.根据id修改数据
        updateById(c);

        //4.生成兑换码
        //4.0为生产的兑换码赋值过期时间
        coupon.setTermEndTime(couponIssueFormDTO.getIssueEndTime());
        //4.1发放优惠券时，生成兑换码只要是待发放状态,或者发放方式是兑换码才需要生成
        if (coupon.getStatus() == CouponStatus.DRAFT || coupon.getObtainWay() == ObtainType.ISSUE) {
            //4.2调用生成兑换码的service生成兑换码
            codeService.exchangeCode(coupon);
        }
    }

    /**
     * 查询发放中的优惠券
     *
     * @return 优惠券数据
     */
    @Override
    public List<CouponVO> queryIssueCoupons() {
        //0.获取用户id
        Long userId = UserContext.getUser();
        //1.查询优惠券表，且状态太是发放中的
        //select * from coupon where status = 3 and obtain_way = 1;
        LocalDateTime now = LocalDateTime.now();
        List<Coupon> coupons = this.lambdaQuery()
                .eq(Coupon::getStatus, CouponStatus.ISSUING)
                .eq(Coupon::getObtainWay, ObtainType.PUBLIC)
                .le(Coupon::getIssueBeginTime, now)
                .ge(Coupon::getIssueEndTime, now)
                .list();
        if (ObjectUtil.isEmpty(coupons)) {
            //为空则返回一个空集合
            return CollUtils.emptyList();
        }
        //2.收集优惠券的id
        List<Long> coupondIds = coupons.stream().map(Coupon::getId).collect(Collectors.toList());
        if (ObjectUtil.isEmpty(coupondIds)) {
            throw new BizIllegalException("优惠券不存在");
        }
        //2.1查询我的user_coupomn表查询我的优惠券
        List<UserCoupon> userCoupons = userCouponService.lambdaQuery()
                .eq(UserCoupon::getUserId, userId)
                .in(ObjectUtil.isNotEmpty(coupondIds), UserCoupon::getCouponId, coupondIds).list();
        //2.2.通过strea流进行分组求数量
        //2.2.1对当前用户的优惠券分组求数量
        Map<Long, Long> userCouponCount =
                userCoupons.stream().collect(Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));
        //2.2.2对当前用户未使用的优惠券分组求数量
        Map<Long, Long> userCouponUnuser =
                userCoupons.stream().filter(u -> u.getStatus() == UNUSED).collect(Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));
        //3.封装数据
        List<CouponVO> voList = new ArrayList<>();
        for (Coupon coupon : coupons) {
            //3.数据拷贝
            CouponVO vo = BeanUtils.copyBean(coupon, CouponVO.class);
            //3.1.分装是否可领取数据 ,当前发放的数量是否超过总数量，是否超过每人领取上限
            if (coupon.getIssueNum() < coupon.getTotalNum() &&
                    userCouponCount.getOrDefault(coupon.getId(), 0L) < coupon.getUserLimit()) {
                vo.setAvailable(true);
            }
            //3.2.分装是否已经领取数据
            if (userCouponUnuser.getOrDefault(coupon.getId(), 0L) > 0) {
                vo.setReceived(true);
            }
            voList.add(vo);
        }
        return voList;
    }

}
